package com.taobao.classloader;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class JarResources {
	private static Logger			logger			= LoggerFactory.getLogger(JarResources.class);
	private Map<String, Integer>	htSizes			= new HashMap<String, Integer>();
	private Map<String, byte[]>		htJarContents	= new HashMap<String, byte[]>();

	private List<String>			jars;

	public JarResources(List<String> jarFilePaths) {
		this.jars = new LinkedList<String>();
		getJarFiles(jarFilePaths, this.jars);
	}

	public boolean containsClass(String className) {
		return htJarContents.containsKey(className.replace('.', '/').concat(".class"));
	}

	public byte[] getResource(String name) {
		return (byte[]) htJarContents.get(name);
	}

	public byte[] getClassResource(String className) {
		return (byte[]) htJarContents.get(className.replace('.', '/').concat(".class"));
	}

	public void init() {
		for (String file : jars) {
			addJarFile(file);
		}
	}

	private List<String> getJarFiles(List<String> jarFilePaths, List<String> jars) {
		for (String path : jarFilePaths) {
			File file = new File(path);
			if (file.exists()) {
				if (file.isFile()) {
					jars.add(file.getAbsolutePath());
				} else if (file.isDirectory()) {
					getJarFiles(Arrays.asList(file.list()), jars);
				}
			} else {
				logger.error("File does not exist: {},full path: {}", path, file.getAbsolutePath());
			}
		}
		return jars;
	}

	private void addJarFile(String jarFilePath) {
		try {
			ZipFile jarFile = new ZipFile(jarFilePath);
			Enumeration<? extends ZipEntry> e = jarFile.entries();
			while (e.hasMoreElements()) {
				ZipEntry ze = (ZipEntry) e.nextElement();
				logger.debug(dumpZipEntry(ze));
				htSizes.put(ze.getName(), new Integer((int) ze.getSize()));
			}
			jarFile.close();

			FileInputStream fis = new FileInputStream(jarFilePath);
			BufferedInputStream bis = new BufferedInputStream(fis);
			ZipInputStream zis = new ZipInputStream(bis);
			ZipEntry ze = null;
			while ((ze = zis.getNextEntry()) != null) {
				if (ze.isDirectory()) {
					continue;
				}
				logger.debug("name={},size={}", ze.getName(), ze.getSize());

				int size = (int) ze.getSize();
				if (size == -1) {
					size = ((Integer) htSizes.get(ze.getName())).intValue();
				}

				byte[] b = new byte[(int) size];
				int rb = 0;
				int chunk = 0;
				while (((int) size - rb) > 0) {
					chunk = zis.read(b, rb, (int) size - rb);
					if (chunk == -1) {
						break;
					}
					rb += chunk;
				}

				htJarContents.put(ze.getName(), b);
				logger.debug("{} rb={},size={},csize={}", new Object[] { ze.getName(), rb, size, ze.getCompressedSize() });
			}
		} catch (NullPointerException e) {
			logger.error("done.", e);
		} catch (FileNotFoundException e) {
			logger.error("error.", e);
		} catch (IOException e) {
			logger.error("error.", e);
		}
	}

	private String dumpZipEntry(ZipEntry ze) {
		StringBuffer sb = new StringBuffer();
		if (ze.isDirectory()) {
			sb.append("d ");
		} else {
			sb.append("f ");
		}

		if (ze.getMethod() == ZipEntry.STORED) {
			sb.append("stored   ");
		} else {
			sb.append("defalted ");
		}

		sb.append(ze.getName());
		sb.append("\t");
		sb.append("" + ze.getSize());
		if (ze.getMethod() == ZipEntry.DEFLATED) {
			sb.append("/" + ze.getCompressedSize());
		}

		return (sb.toString());
	}

	public static void main(String[] args) throws IOException {
		List<String> list = new ArrayList<String>();
		list.add("C:/Users/rmfish/Dev/Workspace/testproj3/lib/testpro-0.0.2-SNAPSHOT.jar");
		list.add("C:/Users/rmfish/Dev/Workspace/testproj3/lib/testproj2-0.0.1-SNAPSHOT.jar");
		JarResources jr = new JarResources(list);
		String className = "org.paralit.version.Version";
		byte[] buff = jr.getClassResource(className);
		if (buff == null) {
			System.out.println("Could not find " + className + ".");
		} else {
			System.out.println("Found " + className + " (length=" + buff.length + ").");
		}
	}
}